package cn.bdqn.filter;

import cn.bdqn.utils.JWTUtils;
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;


@Component
public class JWTFilter implements GlobalFilter, Ordered {


    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        ServerHttpRequest request = exchange.getRequest();

        String token = request.getHeaders().getFirst("token");
        Map<String, Object> map = new HashMap<>();
        try {
            JWTUtils.verify(token);
            return chain.filter(exchange);
        } catch (TokenExpiredException e) {
            map.put("state", false);
            map.put("msg", "Token已经过期!!!");
        } catch (SignatureVerificationException e) {
            map.put("state", false);
            map.put("msg", "签名错误!!!");
        } catch (AlgorithmMismatchException e) {
            map.put("state", false);
            map.put("msg", "加密算法不匹配!!!");
        } catch (Exception e) {
            e.printStackTrace();
            map.put("state", false);
            map.put("msg", "无效token~~");
        }
        try {
            ServerHttpResponse response = new  ServerHttpResponseDecorator(exchange.getResponse()){
                @Override
                public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                    Flux<? extends DataBuffer> fluxBody = Flux.from(body);
                    DataBufferFactory bufferFactory = exchange.getResponse().bufferFactory();
                    return super.writeWith(fluxBody.buffer().map(dataBuffers -> {
                        DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
                        DataBuffer join = dataBufferFactory.join(dataBuffers);
                        byte[] content = new byte[join.readableByteCount()];
                        join.read(content);

                        DataBufferUtils.release(join);
                        String responseData = null;
                        try {
                            responseData = new ObjectMapper().writeValueAsString(map);
                        } catch (JsonProcessingException e) {
                            e.printStackTrace();
                        }
                        byte[] uppedContent = new String(responseData.getBytes(), Charset.forName("UTF-8")).getBytes();
                        return bufferFactory.wrap(uppedContent);

                    }));
                }
            };

            return chain.filter(exchange.mutate().response(response).build());
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 2、如果不存在，则认证失败
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        // 3、直接请求结束返回
//        return exchange.getResponse().setComplete();
        return  chain.filter(exchange);

    }

    // 指定过滤器的执行顺序，返回值越小，执行优先级越高
    @Override
    public int getOrder() {
        return 0;
    }
}
